﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../xos/head.h"
#include "socket_impl.h"

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static socket_impl::POOL * pool_ptr = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    socket_impl::socket_impl()
    {
        init_data();
    }

    socket_impl::~socket_impl()
    {
        term();
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::init_data()
    {
        int ret = 0;
        memset( &m_addr, 0, sizeof( sockaddr_in ) );
        m_hSocket = INVALID_SOCKET;
        m_pUserData = 0;
        return ret;
    }

    int socket_impl::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

    int socket_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int socket_impl::term()
    {
        int nRet = 0;
        init_data();
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // udp server init
    // 
    int socket_impl::udp_init( const char * lpszLocalIp, unsigned short usLocalPort )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            m_hSocket = ::WSASocket( AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED );
            if( INVALID_SOCKET == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = TRUE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, ( const char * )&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszLocalIp, &dwIp, 0 );
        }

        if( 0 == nRet )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usLocalPort );
            nRet = bind( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        return nRet;
    }

    // 
    // udp send
    // 
    int socket_impl::udp_send( const char * lpBuf, int nLen, const char * lpszToIp, unsigned short usToPort )
    {
        using namespace std;
        int nRet = 0;

        int nToLen = sizeof( sockaddr_in );
        sockaddr_in addr = { 0 };
        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszToIp, &dwIp, 0 );
            if( 0 != nRet )
            {
                nRet = -1;
            }
        }

        if( 0 == nRet )
        {
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usToPort );
            nRet = sendto( m_hSocket, lpBuf, nLen, 0, ( const struct sockaddr * )&addr, nToLen );
        }

        return nRet;
    }

    // 
    // udp recv
    // 
    int socket_impl::udp_recv( char * lpBuf, int nLen, char * lpszFromIp, unsigned short * lpusFromPort )
    {
        using namespace std;
        int nRet = 0;
        
        int tFromLen = sizeof( sockaddr_in );
        sockaddr_in addr = { 0 };
        DWORD dwRet = 0;

        nRet = recvfrom( m_hSocket, lpBuf, nLen, 0, ( struct sockaddr * )&addr, &tFromLen );

        if( -1 != nRet )
        {
            //lstrcpyA( lpszFromIp, inet_ntoa( addr.sin_addr ) );
            char buf[100] = { 0 };
            inet_ntop(AF_INET, (const void*)&addr.sin_addr, buf, sizeof(buf));
            lstrcpyA(lpszFromIp, buf );
            if( lpusFromPort )
            {
                *lpusFromPort = ntohs( addr.sin_port );
            }
        }
        else
        {
            dwRet = WSAGetLastError();
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // tcp init
    // 
    int socket_impl::tcp_init( const char * lpszLocalIp, unsigned short usLocalPort, bool bBind )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            m_hSocket = ::WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
            if( INVALID_SOCKET == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            BOOL bOptVal = TRUE;
            int bOptLen = sizeof( bOptVal );
            int ret = setsockopt( m_hSocket, SOL_SOCKET, SO_REUSEADDR, ( const char * )&bOptVal, bOptLen );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( ( 0 == nRet ) && bBind )
        {
            nRet = get_ip_by_name( lpszLocalIp, &dwIp, 0 );
        }

        if( ( 0 == nRet ) && bBind )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usLocalPort );
            nRet = bind( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        return nRet;
    }

    // 
    // tcp accept
    // 
    int socket_impl::tcp_accept( i_socket ** ppv )
    {
        int nRet = 0;

        SOCKET hSocket = INVALID_SOCKET;
        sockaddr_in addr = { 0 };
        int nLen = sizeof( sockaddr_in );
        socket_impl * p = 0;
        *ppv = 0;

        if( 0 == nRet )
        {
            hSocket = accept( m_hSocket, ( sockaddr * )&addr, &nLen );
            if( INVALID_SOCKET == hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            g_pXos->create( i_xos::XOS_OBJ_SOCKET, ( void ** )&p );
            if( !p )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            memcpy( &p->m_addr, &addr, sizeof( sockaddr_in ) );
            p->m_hSocket = hSocket;
            *ppv = p;
        }

        if( 0 != nRet )
        {
            if( INVALID_SOCKET == hSocket )
            {
                closesocket( hSocket );
                hSocket = INVALID_SOCKET;
            }

            if( p )
            {
                p->release();
                p = 0;
            }
        }

        return nRet;
    }

    // 
    // tcp connect
    // 
    int socket_impl::tcp_connect( const char * lpszServerIp, unsigned short usServerPort )
    {
        int nRet = 0;

        unsigned long dwIp = 0;

        if( 0 == nRet )
        {
            nRet = get_ip_by_name( lpszServerIp, &dwIp, 0 );
        }

        if( 0 == nRet )
        {
            m_hSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
            if( INVALID_SOCKET == m_hSocket )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            sockaddr_in & addr = m_addr;
            memset( &addr, 0, sizeof( addr ) );
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = dwIp;
            addr.sin_port = htons( usServerPort );
            nRet = connect( m_hSocket, ( const sockaddr * )&addr, sizeof( sockaddr_in ) );
        }

        if( 0 != nRet )
        {
            if( INVALID_SOCKET != m_hSocket )
            {
                closesocket( m_hSocket );
                m_hSocket = INVALID_SOCKET;
            }
        }

        return nRet;
    }

    // 
    // tcp侦听
    // 
    int socket_impl::tcp_listen( int nNum )
    {
        int nRet = 0;
        nRet = listen( m_hSocket, nNum );
        return nRet;
    }

    // 
    // tcp send
    // 
    int socket_impl::tcp_send( const char * lpBuf, int nLen )
    {
        int nRet = 0;

        nRet = send( m_hSocket, lpBuf, nLen, 0 );

        if( SOCKET_ERROR == nRet )
        {
            int error = WSAGetLastError();
            char buf[1024] = { 0 };
            sprintf_s( buf, "tcp_send error = %d", error );
            OutputDebugStringA( buf );
        }

        return nRet;
    }

    // 
    // tcp recv
    // 
    int socket_impl::tcp_recv( char * lpBuf, int nLen )
    {
        int nRet = 0;

        nRet = recv( m_hSocket, lpBuf, nLen, 0 );

        if( SOCKET_ERROR == nRet )
        {
            int error = WSAGetLastError();
            char buf[1024] = { 0 };
            sprintf_s( buf, "tcp_recv error = %d", error );
            OutputDebugStringA( buf );
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int socket_impl::close( int onoff, int linger )
    {
        int nRet = 0;

        if( INVALID_SOCKET == m_hSocket )
        {
            return 0;
        }
        {
            struct linger so_linger;
            memset( &so_linger, 0, sizeof( so_linger ) );
            so_linger.l_linger = linger;
            so_linger.l_onoff = onoff;
            setsockopt( m_hSocket,
                SOL_SOCKET,
                SO_LINGER,
                (const char *)&so_linger,
                sizeof( so_linger ) );
        }
        closesocket( m_hSocket );
        m_hSocket = INVALID_SOCKET;

        return nRet;
    }

    int socket_impl::shut_down( int how )
    {
        int ret = 0;

        int sd = SD_SEND;

        switch( how )
        {
        case XOS_SD_RECEIVE:
            {
                sd = SD_RECEIVE;
            }
            break;
        case XOS_SD_SEND:
            {
                sd = SD_SEND;
            }
            break;
        case XOS_SD_BOTH:
            {
                sd = SD_BOTH;
            }
            break;
        }

        if( INVALID_SOCKET != m_hSocket )
        {
            shutdown( m_hSocket, sd );
        }

        return ret;
    }

    //
    // 根据机器名得到IP
    //
    int socket_impl::get_ip_by_name( const char * lpszName, unsigned long * lpdwIp, char * lpszIp )
    {
        int nResult = 1;
        int nRet = 0;

        unsigned long dwIp = 0;
        char ip[100] = { 0 };
        bool bIsName = false;

        if( ( 0 == nRet ) && ( !lpszName || ( 0 == strlen( lpszName ) ) ) )
        {
            lstrcpyA( ip, "127.0.0.1" );
            dwIp = htonl( INADDR_ANY );
            nResult = 0;
            nRet = 1;
        }

        if( 0 == nRet )
        { 
            int len = lstrlenA( lpszName );
            for( int i = 0; i < len; i++ )
            {
                bool bState = ( ( lpszName[i] <= '9' ) && ( lpszName[i] >= '0' ) ) || ( lpszName[i] == '.' );
                if( !bState )
                {
                    bIsName = true;
                    break;
                }
            }
        }

        ///////////////////////////////////////
        //得到IP

        if( 0 == nRet )
        {
            if( bIsName )
            {
                nRet = get_ip_by_name( lpszName, ip );
            }
            else
            {
                lstrcpyA( ip, lpszName );
            }
        }

        if( 0 == nRet )
        {
            IN_ADDR addr = { 0 };
            memset(&addr, 0, sizeof(addr));
            //dwIp = inet_addr( ip );
            inet_pton(AF_INET, ip, (void*)&addr);
            dwIp = addr.s_addr;
        }

        if( 0 == nRet )
        {
            nResult = 0;
        }

        if( 0 == nResult )
        {
            if( lpszIp )
            {
                lstrcpyA( lpszIp, ip );
            }
            if( lpdwIp )
            {
                *lpdwIp = dwIp;
            }
        }

        return nResult;
    }

    int socket_impl::get_ip_by_name( const char * lpszName, char * lpszIP )
    {
        int nRet = 0;

        IN_ADDR* lpAddr = 0;

        if( 0 == nRet )
        {
            struct addrinfo hints, * res = 0;

            memset(&hints, 0, sizeof(hints));

            hints.ai_family = AF_INET;     /* Allow IPv4 */
            hints.ai_flags = AI_PASSIVE;   /* For wildcard IP address */
            hints.ai_protocol = 0;         /* Any protocol */
            hints.ai_socktype = SOCK_STREAM;

            //lpHost = gethostbyname( lpszName );
            int ret = getaddrinfo(lpszName, NULL, &hints, &res);
            if (-1 != ret)
            {
                SOCKADDR_IN* pSockAddr = ( SOCKADDR_IN* )res->ai_addr;
                lpAddr = &pSockAddr->sin_addr;
            }
            else
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            char buf[100] = { 0 };
            inet_ntop(AF_INET, (const void*)lpAddr, buf, sizeof(buf));
            lstrcpyA( lpszIP, buf );
        }

        return nRet;
    }

    int socket_impl::get_local_addr( char * lpszName, char * lpszIP )
    {
        int nRet = 0;

        char name[1024] = { 0 }, ip[1024] = { 0 };

        if( 0 == nRet )
        {
            nRet = gethostname( name, sizeof( name ) );
        }

        if (0 == nRet)
        {
            nRet = get_ip_by_name( name, ip );
        }

        {
            if( lpszName )
            {
                lstrcpyA( lpszName, name );
            }
            if( lpszIP )
            {
                lstrcpyA( lpszIP, ip );
            }
        }

        return nRet;
    }

    // 
    // 
    // 
    int socket_impl::get_socket_addr( char * lpszIp, unsigned short * lpusPort )
    {
        int nRet = 0;

        sockaddr_in addr = { 0 };
        int addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getsockname( m_hSocket, ( sockaddr * )&addr, &addr_len );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            char buf[100] = { 0 };
            inet_ntop(AF_INET, (const void*)&addr.sin_addr, buf, sizeof(buf));
            lstrcpyA( lpszIp, buf );
            *lpusPort = ntohs( addr.sin_port );
        }

        return nRet;
    }

    int socket_impl::get_peer_addr( char * lpszIp, unsigned short * lpusPort )
    {
        int nRet = 0;

        sockaddr_in addr = { 0 };
        int addr_len = sizeof( addr );

        if( 0 == nRet )
        {
            int ret = getpeername( m_hSocket, ( sockaddr * )&addr, &addr_len );
            if( SOCKET_ERROR == ret )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            char buf[100] = { 0 };
            inet_ntop(AF_INET, (const void*)&addr.sin_addr, buf, sizeof(buf));
            lstrcpyA( lpszIp, buf );
            *lpusPort = ntohs( addr.sin_port );
        }

        return nRet;
    }

    // 
    // 
    // 
    int socket_impl::get_ip_port( char * lpszIp, unsigned short * lpusPort )
    {
        int nRet = 0;

        char buf[100] = { 0 };
        inet_ntop(AF_INET, (const void*)&m_addr.sin_addr, buf, sizeof(buf));
        lstrcpyA( lpszIp, buf );
        *lpusPort = ntohs( m_addr.sin_port );

        return nRet;
    }

    void socket_impl::set_addr( void * lpAddr )
    {
        memcpy( &m_addr, lpAddr, sizeof( sockaddr_in ) );
    }

    void socket_impl::set_socket( xos::xos_u64 ulSocket )
    {
        m_hSocket = ( SOCKET )ulSocket;
    }

    xos_u64 socket_impl::get_socket()
    {
        return ( xos_u64 )m_hSocket;
    }

    int socket_impl::set_async( bool bAsync )
    {
        int nRet = 0;
        unsigned long ulValue = bAsync ? 1 : 0;
        nRet = ioctlsocket( m_hSocket, FIONBIO, &ulValue );
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get user data
    // 
    void * socket_impl::get_user_data( void ** ppvUserData )
    {
        void * pRet = m_pUserData;

        if( ppvUserData )
        {
            *ppvUserData = m_pUserData;
        }

        return pRet;
    }

    // 
    // set user data
    // 
    int socket_impl::set_user_data( void * pUserData )
    {
        int nRet = 0;
        m_pUserData = pUserData;
        return nRet;
    }


} // xos
